import ILP_CLASS as ilpc
import MYGRAPH_CLASS as mgc
import random as ra
from cplex.exceptions import CplexError

class Time_Solve():
    #Group sizes will be calculated by ~Binomial(2*Expected_Group_Size, .5)
    #Groups are equally likely to appear each minute with the appropriate probability
    #Group duration is also calculated with ~Binomial(2*Expected
    def __init__(self, DC_Num, Init_Group_List = [], Max_Lat = 600, Path_Length = 2, \
                 Duration = float("inf"), Expected_Groups_Per_Min = 1.0/12.0, \
                 Expected_Group_Size = 5, Expected_Group_Duration = 60):
        
        
        #This creates the graph that is held in the Time_Solve class
        self.Graph = mgc.MyGraph(Init_Group_List, DC_Num, Max_Lat)
        self.Graph.Set_Latencies()
        self.Graph.Set_DC_Owners()
        self.Graph.Set_Edge_Costs()
        self.Graph.Set_DC_Costs()
        self.Graph.Set_Max_Lats()
        self.Graph.Expected_Group_Duration = 60
        self.Solution = True
        self.Time_Feasable = 0
        self.Cost = None
        
        self.Path_Length = Path_Length
        self.Duration = Duration
        self.Expected_Groups_Per_Min = Expected_Groups_Per_Min
        self.Expected_Group_Size = Expected_Group_Size
        #We'll keep track of the current time in 'minutes'
        self.Current_Time = 0
        
        #This creats the initial ILP out of our initally specified graph
        self.ILP = ilpc.Ilp(self.Graph, self.Path_Length)
        
    ########################################################################################
    #This function solves the ILP for the current graph state
    #Returns True if a solution is found, False otherwise
    def Solve_For_Current_Graph_State(self):
        self.ILP = ilpc.Ilp(self.Graph, self.Path_Length)
        self.ILP.Set_Objective()   
        self.ILP.Add_Latency_Constraints()
        try:
            self.ILP.Solve_Problem()
            Soln = self.ILP.Prob.solution.get_values()
            self.Solution = True
            self.Cost = self.ILP.Get_Soln()
            return True
        except CplexError:
            print("No Solution")
            self.Solution = False
            return False
        
    ########################################################################################
    def Print_Current_Config(self):
        if self.Solution is True:
            self.ILP.Print_Solution()
        else:
            print("The Last Iteration had No solution")
            
    #######################################################################################
    #this lets us control whether or not the DC numbers are changed based on the
    #problem finding a solution or not
    def Modify_DCs(self):
        if self.Solution is True:
            self.ILP.Adjust_DC_Nums(self.Current_Time)
        else:
            print("The Last Iteration had No solution, so no DC's were rented")
            
    #######################################################################################
    #This runs the problem, inserting groups where appropriate, then calculating the new
    #ilp and printing the results.
    def Run_Problem(self):
        print("BEGINNING")
        First_Change = True
        while self.Current_Time < self.Duration:
            print(self.Current_Time)
        
            Changes = False
            #Check if any current Groups need to be removed when their times are up
            for My_Group in self.Graph.Group_List:
                if self.Current_Time == My_Group.Time_Done:
                    self.Graph.Remove_Group(My_Group)
                    Changes = True
        
            #Check (using Random Numbers...) if any additional groups arrive
            if ra.uniform(0, 1) < self.Expected_Groups_Per_Min:
                self.Graph.Add_Group(mgc.Binomial(2*self.Expected_Group_Size, .5), self.Current_Time)
                self.Graph.Set_Edge_Costs()
                Changes = True
                
            #Checks if any DC's expire in this iteration
            for DC in self.Graph.DC_List:
                i = 0
                while i < len(DC.Expiration_Times):
                    if DC.Expiration_Times[i] == self.Current_Time:
                        DC.Expiration_Times.remove(DC.Expiration_Times[i])
                        DC.Num_Rented = DC.Num_Rented - 1
                    else:
                        i = i + 1
                
            #Recalculates, prints if any changes were made
            if Changes is True:
                First_Change = False
                self.Graph.Set_Max_Lats()
                self.ILP = ilpc.Ilp(self.Graph, self.Path_Length)
                self.ILP.Set_Objective()
                self.Solve_For_Current_Graph_State()
                self.Modify_DCs()
                self.Print_Current_Config()
                
            if self.Solution is True:
                self.Time_Feasable = self.Time_Feasable + 1
                
            #Increment Time Counter
            self.Current_Time = self.Current_Time + 1
            
        return self.Time_Feasable
                
        
                
        
            
            
    
            
        
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
        